#include "Layer.hpp"
#include <vector>
#include <list>
#include <boost/thread.hpp>
#include "Bits.h"
#include <iostream>
#include <cstdio>

//using namespace std;
using namespace boost;
using namespace ufpb::project::network;

namespace ufpb {

    namespace project {

        namespace network {

            
            void funcThreadEnvia(Layer* layer) {

                boost::unique_lock<boost::mutex> lock(*layer->datas);
                while (true) {

		    //std::cout << "Thread Envia" << std::endl;
		    while (layer->sendBuffer->empty()) {

			//std::cout << "Thread Envia Waiting" << std::endl;
			layer->cvs->wait(lock);
		    }
		    //std::cout << "Thread Envia Processing" << std::endl;
                    layer->downMutex->lock();
                    Bits* e = layer->sendBuffer->front();
                    layer->sendBuffer->pop_front();
                    layer->downMutex->unlock();
                    layer->newMessageToSend(e);
                }
            }


            void funcThreadRecebe(Layer* layer) {

		boost::unique_lock<boost::mutex> lock(*layer->datar);
                while (true) {

		    //std::cout << "Thread Recebe" << std::endl;
		    while (layer->receiveBuffer->empty()) {

			//std::cout << "Thread Recebe Waiting" << std::endl;
			layer->cvr->wait(lock);
		    }
		    //std::cout << "Thread Recebe Processing" << std::endl;
                    layer->upMutex->lock();
                    Bits* e = layer->receiveBuffer->front();
                    layer->receiveBuffer->pop_front();
                    layer->upMutex->unlock();
		    layer->newReceivedMessage(e);
                }
            }
            
            //static const Layer::BUFFER = 50;
            //static Layer::BUFFER = 50;

            Layer::Layer(std::vector<int>* config) {

                this->config = config;
                //this->sendBuffer = new std::list<Bits*>(BUFFER);
		this->sendBuffer = new std::list<Bits*>(0);
		//this->receiveBuffer = new std::list<Bits*>(BUFFER);
		this->receiveBuffer = new std::list<Bits*>(0);
		this->datar = new boost::mutex();
		this->datas = new boost::mutex();
		this->cvr = new boost::condition_variable();
		this->cvs = new boost::condition_variable();
                this->downMutex = new boost::mutex();
                this->upMutex = new boost::mutex();
                this->sendt = 0;
                this->receivet = 0;
            }

            Layer::~Layer() {

		//delete this->sendBuffer;		
            }

            void Layer::init(void) {

                if (this->sendt == 0) {

                    this->sendt = new boost::thread(funcThreadEnvia, this);
                }

               if (this->receivet == 0) {

                   this->receivet = new boost::thread(funcThreadRecebe, this);
               }
            }

            void Layer::setDownLayer(Layer* layer){

                this->layerDown = layer;
            }

            void Layer::setUpLayer(Layer* layer){

                this->layerUp = layer;
            }

            Layer* Layer::getDownLayer(){

				return this->layerDown;
			}

			Layer* Layer::getUpLayer(){

				return this->layerUp;
			}

            void Layer::receive(Bits* data) {

		//std::cout << "Receiving Data" << std::endl;
                this->upMutex->lock();
		//std::cout << "After lock r" << std::endl;		
                this->receiveBuffer->push_back(data);

		//std::cout << "Notifing r" << std::endl;

		//std::cout << "Unlocking r" << std::endl;
                this->upMutex->unlock();
//                boost::lock_guard<boost::mutex> lock(*this->datar);
                //std::cout << "Data received" << std::endl;
                this->cvr->notify_all();
            }

            void Layer::send(Bits* data) {

		//std::cout << "Sending Data" << std::endl;
                this->downMutex->lock();

		//std::cout << "After lock s" << std::endl;
                this->sendBuffer->push_back(data);

		//std::cout << "Unlocking s" << std::endl;
                this->downMutex->unlock();
		//std::cout << "Data sent" << std::endl;
//                boost::lock_guard<boost::mutex> lock(*this->datas);
				//std::cout << "Notifing s" << std::endl;
				this->cvs->notify_all();
            }

            
        }
    }
}
